/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

#include "geocoder.h"
#include "vcConfig.h"
#include "socket.h"

#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/framework/Wrapper4InputSource.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>

#include "GeocodeResponse.hxx"

XERCES_CPP_NAMESPACE_USE

using std::auto_ptr;
using namespace yahoo;
using xml_schema::flags;

/*<---Extern Variables---->*/
extern GuiStruct  guiStr;
extern bool verbose;

Geocoder::Geocoder() {
	init_socket();
}

Geocoder::~Geocoder() {
}

void Geocoder::init_socket() {
	sock = NULL;

	if( sock != NULL ) {
		sock->close();
		delete sock;
		sock = NULL;
	}

	sock = new Socket("local.yahooapis.com", "80", Socket::TCP);
	if( sock == NULL ) {
		cout << "Geocoder::init() - could not get new socket, " <<  strerror(errno) << endl;
	} else
		sock->setNonBlocking();
}

void Geocoder::search(const char * misc) {
	char html_misc[256];  //FIXME -- how big is this buffer?
	size_t j = 0;

	for(size_t i=0; i<strlen(misc); i++) {
		if( misc[i] == ' ' ) {
			html_misc[j++] = '%';
			html_misc[j++] = '2';
			html_misc[j++] = '0';
		}
		else {
			html_misc[j] = misc[i];
			j++;
		}
	}
	html_misc[j]=0;

	String api_key = String("YVpC2O3V34HHW7rNdc2EialdATMDshCOQBmX.cEx1znakIhQpVUoP99VhkCaSoDfGgG4IKuAjg--");
	String query = String("GET /MapsService/V1/geocode?appid=") + api_key;
	query += String("&location=") + String(html_misc); 
	query +=	String(" HTTP/1.1\n");
	query +=	String("Host: local.yahooapis.com\n\n");
	send_query(query);
}

void Geocoder::search(const char * city, const char * state) {
	char html_city[128];  //FIXME -- how big is this buffer?
	char html_state[128];
	size_t j = 0;

	for(size_t i=0; i<strlen(city); i++) {
		if( city[i] == ' ' ) {
			html_city[j++] = '%';
			html_city[j++] = '2';
			html_city[j++] = '0';
		}
		else {
			html_city[j] = city[i];
			j++;
		}
	}
	html_city[j]=0;

	j = 0;
	for(size_t i=0; i<strlen(state); i++) {
		if( state[i] == ' ' ) {
			html_state[j++] = '%';
			html_state[j++] = '2';
			html_state[j++] = '0';
		}
		else {
			html_state[j] = state[i];
			j++;
		}
	}
	html_state[j]=0;

	//http://local.yahooapis.com/MapsService/V1/geocode?appid=YD-9G7bey8_JXxQP6rxl.fBFGgCdNjoDMACQA--&street=701+First+Ave&city=Sunnyvale&state=CA

	String api_key = String("YVpC2O3V34HHW7rNdc2EialdATMDshCOQBmX.cEx1znakIhQpVUoP99VhkCaSoDfGgG4IKuAjg--");
	String query = String("GET /MapsService/V1/geocode?appid=") + api_key;
	query += String("&city=") + String(html_city); 
	query +=	String("&state=") + String(html_state);
	query +=	String(" HTTP/1.1\n");
	query +=	String("Host: local.yahooapis.com\n\n");
	send_query(query);
}

void Geocoder::send_query(String query) {

	int tries = 0;

	if( sock->isClosed() ) {
		if ( !sock->open( Socket::DUPLEX_CLIENT ) ) {
			cout << "Geocoder::search() - Could not open sock" << endl; 
			return;
		} 
	} 

	while(!sock->isConnected() && tries < 1) {
		if(!sock->isConnected()) {
			if(verbose)
				cout << "Geocoder::search Trying to connect ... " << flush;
			sock->connectHost();
		}

		if(!sock->isConnected()) {
			if(verbose)
				cout << "                          [ FAIL ]" << endl;
			tries++;
			sleep(1);
		}
	}

	if(!sock->isConnected()) return;

	Socket::SocketWait status = Socket::WAIT_TIMEOUT;

	sock->write(query.c_str(), query.length());
	status = sock->wait(GEO_CONNECTION_TIMEOUT * 1000);

	int n = 0;
	switch (status) {
		case Socket::WAIT_PEER:
			n = sock->read(data, FIFO_BUF_SIZE);
			if( n > 0) { // data available
				String result =  String(data);
				int i = result.find("<?xml");
				strncpy(data, &(data[i]), n-i);
				data[n-i+1] = 0;
				result = String(data);
				n = result.find("<!--");
				parse(n);
			} else {
				if( n == 0);
				else if( errno != EWOULDBLOCK || errno != EINTR) 
					cout << "Geocoder::update() - bad read from client, " <<  strerror(errno) << endl;
			}
			break;
		case Socket::WAIT_INT:
			cout << "Geocoder::update() - WAIT_INT, " <<  strerror(errno) << endl;
			break;
		case Socket::WAIT_TIMEOUT:
			cout << "Geocoder::update() - socket timeout, " <<  strerror(errno) << endl;
			break;
		case Socket::WAIT_ERROR:
			cout << "Geocoder::update() - socket error, " <<  strerror(errno) << endl;
			break;
		case Socket::WAIT_DATA:
			cout << "Geocoder::update() - WAIT_DATA, " <<  strerror(errno) << endl;
			break;
	}
	sock->close();
}

void Geocoder::parse(int n) {
	try {
		xercesc::XMLPlatformUtils::Initialize();
	}
	catch (const XMLException& toCatch) {
		perror("Geocoder::init() - could not initialize xerces");
	}

	XercesDOMParser* parser = new XercesDOMParser();
	parser->setValidationScheme(XercesDOMParser::Val_Never);
	parser->setDoNamespaces(true);

	ErrorHandler* errHandler = (ErrorHandler*) new HandlerBase();
	parser->setErrorHandler(errHandler);

	MemBufInputSource is ((uint8_t*)data, n, "tempfile", false);
	Wrapper4InputSource wis (&is, false);

	try {
		parser->parse(is);
	}
	catch (const XMLException& toCatch) {
		char* message = XMLString::transcode(toCatch.getMessage());
		cout << "Exception message is: \n"
			<< message << "\n";
		XMLString::release(&message);
		return;
	}
	catch (const DOMException& toCatch) {
		char* message = XMLString::transcode(toCatch.msg);
		cout << "Exception message is: \n"
			<< message << "\n";
		XMLString::release(&message);
		return;
	}
	catch (...) {
		cout << "Unexpected XML Exception \n" ;
		return;
	}

	DOMDocument* document = parser->getDocument();

	try {

		//std::auto_ptr <maps::ResultSet> xml_data_ptr (maps::ResultSet(*document));
		DOMElement* element = document->getDocumentElement();
		maps::ResultSet xml_data(*element);
		for (maps::ResultSet::Result_const_iterator i (xml_data.Result() .begin ());
				i != xml_data.Result ().end ();
				++i) {
			double lat = i->Latitude();
			double lon = i->Longitude();

			cout << "Geocoder moving map to [" << lat << "," << lon << "]" << endl;
			guiStr.config.gpsmap->stopNodeTracking();
			guiStr.config.gpsmap->moveToGeo(lon, lat, 1);
			break;  // only take the first result for now
		}
	}
	catch(::xsd::cxx::tree::unexpected_element<char>  &e)
	{
		// not a Task
	}
	catch(...)
	{
		cout << "Malformatted XML \n" << endl << flush;
		return;
	}

}

void Geocoder::move_map() {
}
